# -*-coding:utf-8-*-
import os
import shutil
import re

from typing import List

OS_TYPE = "2"
"""使用环境(运行前请确认修改) 
1：Windows
2：Windows + WSL
3：类Unix(Linux、MacOS、BSD)
"""

JAR_PATH = "D:\\ja-netfilter\\ja-netfilter.jar"
# JAR_PATH = "/Users/kuibug/Documents/ja-netfilter/ja-netfilter.jar"
"""ja-netfilter 存放路径(运行前请确认修改)"""

IDE_PATH = "D:\\JetBrains\\"
# IDE_PATH = '/Users/kuibug/Library/Application Support/JetBrains/Toolbox/apps'
"""ide 安装路径(运行前请确认修改)"""

LAST_LINE = "-javaagent:" + JAR_PATH
"""需要写入的字符串"""


def conv_path_win2wsl():
    """ WSL(Windows) 路径转换，JAR_PATH、IDE_PATH 转换为 wsl 路径 """
    print("debug：检测到 Windows 路径")
    global JAR_PATH
    global IDE_PATH
    # fix 当IDE和ja-netfilter不在一个盘会报错
    driver_num = re.search(r'^([a-zA-Z]):*', JAR_PATH).group(0)
    wsl_mnt = "/mnt/" + driver_num[0].lower()
    JAR_PATH = JAR_PATH.replace(driver_num, wsl_mnt).replace('\\', '/')

    driver_num = re.search(r'^([a-zA-Z]):*', IDE_PATH).group(0)
    wsl_mnt = "/mnt/" + driver_num[0].lower()
    IDE_PATH = IDE_PATH.replace(driver_num, wsl_mnt).replace('\\', '/')


def conv_path_wsl2win(driver_num: str):
    """ WSL(Linux) 路径转换，LAST_LINE 转换为 windows 路径"""
    print("debug：检测到 Linux 路径")
    win_path = JAR_PATH
    win_path = win_path.replace('/mnt/' + driver_num, driver_num.upper() + ":")
    win_path = win_path.replace('/', '\\')
    global LAST_LINE
    LAST_LINE = "-javaagent:" + win_path


def converted_path():
    """ 专门给wsl环境IDE准备的路径转换 """
    if OS_TYPE == '1' or OS_TYPE == '3':
        # 正常情况下直接采用这两个路径就OK
        pass
    elif OS_TYPE == '2':
        # 如果是 wsl 环境，在填充和检测文本字符的时候需要转换为 Windows写法，其余时间保持Linux
        search = re.search(r'^([a-zA-Z]):*', JAR_PATH)
        if search is None:
            search = re.search(r'/mnt/([a-zA-Z])/*', JAR_PATH)
            if search is None:
                raise FileNotFoundError("错误的wsl路径")
            conv_path_wsl2win(search.group(1))
        else:
            conv_path_win2wsl()
    else:
        raise NameError("不支持的操作系统")


def get_all_file(path: str, count=0, deep=5) -> List[str]:
    """
    遍历路径 bin 目录下的 vmoptions 配置文件
    :param path: 需要检查的文件路径
    :param count: 递归深度计数，默认0
    :param deep: 最大遍历深度，默认5
    :return: file_list 需要修改的文件列表
    """
    print('checking => ' + path)
    path_list = []
    file_list = os.listdir(path)
    for file in file_list:
        file_path = os.path.join(path, file)
        # Mac 上 vmoptions 只有一个在support文件夹下
        if OS_TYPE == '3' and os.path.isfile(file_path) and os.path.splitext(file)[-1] == '.vmoptions':
            path_list.append(file_path)
            # return path_list
        # Windows 上 vmoptions 有两个 bin 目录下的那个才是我们要的
        # pref 在Toolbox 1.27 开始 Toolbox 会复制 ch-x/version.vmoptions 内的配置
        if (OS_TYPE == '1' or OS_TYPE == '2') and os.path.isfile(file_path) and os.path.splitext(file)[-1] == '.vmoptions':
            path_list.append(file_path)
            # return path_list
        # 没找到 vmoptions 就继续往下遍历
        if os.path.isdir(file_path) and count < deep:
            path_list.extend(get_all_file(file_path, count + 1, deep=deep))
    return path_list


def change_file(file_list: List[str], words: str):
    """
    检测文件是否包含指定内容，不包含则追加
    :param file_list: 需要修改的文件列表(全路径)
    :param words: 需要追加的内容
    """
    for file in file_list:
        with open(file, 'a+') as f:
            f.seek(0)
            need_rewrite = True
            for line in f.readlines():
                if words in line:
                    need_rewrite = False
                    print("pass:" + file)
                    break
            if need_rewrite:
                print('rewrite:' + file)
                f.write('\n--add-opens=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED'
                        '\n--add-opens=java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED'
                        '\n' + words)


def check_jar(jar_path: str):
    """
    检查ja-netfilter是否存在
    :param jar_path: 存放路径
    :return:
    """
    source = os.path.abspath('./ja-netfilter')
    target = os.path.dirname(JAR_PATH)
    if len(target) == 0:
        raise FileExistsError("目标路径解析错误,请检查OS_TYPE是否正确")
    if os.path.exists(jar_path) and os.path.isfile(jar_path):
        print("pass: ja-netfilter 已存在！")
    else:
        print("copy file " + source + " to " + target)
        copytree(source, target)


def copytree(src, dst, symlinks=False, ignore=None, copy_function=shutil.copy2, ignore_dangling_symlinks=False):
    """
    copy from shutil.py

    移除了方法 shutil.copystat(src, dst)；
    wsl 中 windows 目录挂载后权限是通过DrvFS权限计算的，直接复制权限会出问题。
    https://learn.microsoft.com/zh-cn/windows/wsl/file-permissions
    """
    names = os.listdir(src)
    if ignore is not None:
        ignored_names = ignore(src, names)
    else:
        ignored_names = set()
    if not os.path.exists(dst):
        os.makedirs(dst)
    errors = []
    for name in names:
        if name in ignored_names:
            continue
        srcname = os.path.join(src, name)
        dstname = os.path.join(dst, name)
        try:
            if os.path.islink(srcname):
                linkto = os.readlink(srcname)
                if symlinks:
                    # We can't just leave it to `copy_function` because legacy
                    # code with a custom `copy_function` may rely on copytree
                    # doing the right thing.
                    os.symlink(linkto, dstname)
                    shutil.copystat(srcname, dstname, follow_symlinks=not symlinks)
                else:
                    # ignore dangling symlink if the flag is on
                    if not os.path.exists(linkto) and ignore_dangling_symlinks:
                        continue
                    # otherwise let the copy occurs. copy2 will raise an error
                    if os.path.isdir(srcname):
                        copytree(srcname, dstname, symlinks, ignore,
                                 copy_function)
                    else:
                        copy_function(srcname, dstname)
            elif os.path.isdir(srcname):
                copytree(srcname, dstname, symlinks, ignore, copy_function)
            else:
                # Will raise a SpecialFileError for unsupported file types
                copy_function(srcname, dstname)
        # catch the Error from the recursive copytree so that we can
        # continue with other files
        except shutil.Error as err:
            errors.extend(err.args[0])
        except OSError as why:
            errors.append((srcname, dstname, str(why)))
    # try:
    #     shutil.copystat(src, dst)
    # except OSError as why:
    #     # Copying file access times may fail on Windows
    #     if getattr(why, 'winerror', None) is None:
    #         print(why)
    #         errors.append((src, dst, str(why)))
    if errors:
        raise shutil.Error(errors)
    return dst


if __name__ == '__main__':
    print("Tips: 运行前请检查并确认IDE安装目录")
    converted_path()
    # 1.先检查 ja-netfilter
    check_jar(JAR_PATH)
    # 2.遍历查找配置文件。如果你的Toolbox版本过低请适当放大deep参数
    all_file = get_all_file(IDE_PATH, deep=3)
    print("\ncheck finsh")
    # 3.修改配置文件内容
    change_file(all_file, LAST_LINE)
    # https://jetbra.in
